package Window;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Decorations;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolTip;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import Command.LDrawColor;
import Command.LDrawColorT;
import Command.LDrawPart;
import Common.Vector3f;
import ConnectivityEditor.Window.ConnectivityEditorUI;
import Exports.PartDomainT;
import LDraw.Support.ColorLibrary;
import LDraw.Support.ConnectivityLibrary;
import LDraw.Support.LDrawUtilities;
import LDraw.Support.PartCache;
import LDraw.Support.type.LDrawGridTypeT;
import Resource.ResourceManager;
import UndoRedo.DirectiveAction;
import UndoRedo.LDrawUndoRedoManager;
public class PartBrowserUI implements DragSourceListener {
BrickViewer preview;
Label label_Preview;
Tree tree;
ToolTip tooltip;
PartCache cache;
Button colorButton;
String lastSearch;
PartDomainT lastSearchDomain;
Image folderImage;
Display display;
Shell shell;
Composite parent;
Composite previewGroup;
private HashMap<String, ArrayList<String>> partListMap;
public PartBrowserUI(Composite parent, int style) {
display = parent.getDisplay();
shell = parent.getShell();
tooltip = new ToolTip(shell, SWT.NONE);
tooltip.setAutoHide(true);
partListMap = new HashMap<String, ArrayList<String>>();
generateMainView(parent);
initData();
}
private void updatePartList(String searchText, PartDomainT searchDomain) {
partListMap.clear();
ArrayList<String> categories = cache.getCategories();
ArrayList<String> list;
for (String categoryName : categories) {
list = cache.getPartLists(categoryName, searchText, searchDomain);
partListMap.put(categoryName, list);
}
}
private void updatePartList() {
partListMap.clear();
ArrayList<String> categories = cache.getCategories();
ArrayList<String> list;
for (String categoryName : categories) {
list = cache.getPartLists(categoryName, lastSearch,
lastSearchDomain);
partListMap.put(categoryName, list);
}
}
public void close() {
}
private void initData() {
BusyIndicator.showWhile(display, new Runnable() {
public void run() {
cache = PartCache.getInstance();
folderImage = ResourceManager.getInstance().getImage(display,
"/Resource/Image/folder_brick.png");
if (!display.isDisposed()) {
display.asyncExec(new Runnable() {
@Override
public void run() {
if (!tree.isDisposed()) {
shell.setCursor(null);
updatePartList(null, null);
updateListView();
}
}
});
}
}
});
}
private void generateMainView(Composite composite) {
parent = composite;
SashForm sashForm = new SashForm(composite, SWT.NONE);
sashForm.setOrientation(SWT.VERTICAL);
Composite treeGroup = new Composite(sashForm, SWT.BORDER);
treeGroup.setLayout(new GridLayout());
Composite searchGroup = new Composite(treeGroup, SWT.NONE);
GridData data = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
searchGroup.setLayoutData(data);
searchGroup.setLayout(new GridLayout(3, false));
final CCombo combo_searchDomain = new CCombo(searchGroup, SWT.NONE);
combo_searchDomain.add("All");
combo_searchDomain.add("LDraw");
combo_searchDomain.add("Bricklink");
combo_searchDomain.select(0);
combo_searchDomain.setEditable(false);
final Text text = new Text(searchGroup, SWT.NONE);
text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
final Listener searchListener = new Listener() {
@Override
public void handleEvent(Event event) {
PartDomainT searchDomain = null;
try {
if (combo_searchDomain.getText().toLowerCase()
.equals("ldraw"))
searchDomain = PartDomainT.LDRAW;
if (combo_searchDomain.getText().toLowerCase()
.equals("bricklink"))
searchDomain = PartDomainT.BRICKLINK;
} catch (Exception e) {
}
search(tree.getItems(), text.getText().toLowerCase(),
searchDomain);
}
};
text.addListener(SWT.KeyUp, searchListener);
combo_searchDomain.addListener(SWT.Selection, searchListener);
tree = new Tree(treeGroup, SWT.NONE);
data = new GridData(SWT.FILL, SWT.FILL, true, true);
tree.setLayoutData(data);
tree.addMenuDetectListener(new MenuDetectListener() {
@Override
public void menuDetected(MenuDetectEvent arg0) {
// TODO Auto-generated method stub
TreeItem[] selectedItem = tree.getSelection();
tree.setMenu(null);
if (selectedItem.length == 1) {
String filename = (String) selectedItem[0].getData();
if (filename != null) {
tree.setMenu(createPopupMenu_Brick(parent.getShell(),
filename));
} else
tree.setMenu(createPopupMenu_Category(
parent.getShell(), selectedItem[0].getText()));
} else {
tree.setMenu(createPopupMenu_Category(parent.getShell(),
null));
}
}
});
tree.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent event) {
TreeItem[] selectedItem = tree.getSelection();
if (selectedItem.length > 0) {
String displayPartName = null;
TreeItem parentItem = null;
int index;
if (selectedItem[0].getData() == null) {
parentItem = selectedItem[0];
index = 0;
} else {
parentItem = selectedItem[0].getParentItem();
index = parentItem.indexOf(selectedItem[0]);
}
Object data;
TreeItem[] items = parentItem.getItems();
for (int i = index; i < parentItem.getItemCount(); i++) {
data = items[i].getData();
if (data != null) {
displayPartName = data.toString();
break;
}
}
updatePreview(displayPartName);
}
}
@Override
public void widgetDefaultSelected(SelectionEvent event) {
}
});
tree.addMouseListener(new MouseListener() {
@Override
public void mouseUp(MouseEvent event) {
}
@Override
public void mouseDown(MouseEvent event) {
}
@Override
public void mouseDoubleClick(MouseEvent event) {
TreeItem[] selectedItem = tree.getSelection();
if (selectedItem.length > 0) {
Object partName = selectedItem[0].getData();
if (partName != null) {
addBrick(partName.toString());
}
}
}
});
tree.addMouseTrackListener(new MouseTrackListener() {
@Override
public void mouseHover(MouseEvent e) {
Point point = new Point(e.x, e.y);
TreeItem item = tree.getItem(point);
if (item != null) {
Object data = item.getData();
if (data != null) {
tree.setToolTipText(data.toString());
return;
}
}
tree.setToolTipText("");
}
@Override
public void mouseExit(MouseEvent e) {
}
@Override
public void mouseEnter(MouseEvent e) {
}
});
tree.addTreeListener(new TreeListener() {
Image connectivityImage = ResourceManager.getInstance().getImage(
display, "/Resource/Image/chain.png");
Image noConnectivityImage = ResourceManager.getInstance().getImage(
display, "/Resource/Image/chain_exclamation.png");
@Override
public void treeExpanded(final TreeEvent e) {
Thread thread = new Thread() {
@Override
public void run() {
if (!display.isDisposed()) {
display.asyncExec(new Runnable() {
@Override
public void run() {
if (!tree.isDisposed()) {
ConnectivityLibrary library = ConnectivityLibrary
.getInstance();
TreeItem item = (TreeItem) e.item;
String fileName;
for (TreeItem subItem : item.getItems()) {
fileName = (String) subItem
.getData();
if (fileName == null
|| subItem.getImage() != null) {
continue;
} else if (library
.hasConnectivity(fileName)) {
subItem.setImage(connectivityImage);
} else {
subItem.setImage(noConnectivityImage);
}
}
}
}
});
}
super.run();
}
};
thread.start();
}
@Override
public void treeCollapsed(TreeEvent e) {
}
});
setDragAndDrop();
DragSource source = new DragSource(tree, DND.DROP_COPY | DND.DROP_MOVE);
source.setTransfer(new Transfer[] { TextTransfer.getInstance() });
source.addDragListener(this);
Composite preview = new Composite(sashForm, SWT.BORDER);
generatePreview(preview);
sashForm.setWeights(new int[] { 2, 1 });
}
private String getFirstPartName() {
TreeItem[] treeItems = tree.getItems();
String displayPartName = null;
if (treeItems.length > 0) {
TreeItem parentItem = null;
int index;
if (treeItems[0].getData() == null) {
parentItem = treeItems[0];
index = 0;
} else {
parentItem = treeItems[0].getParentItem();
index = parentItem.indexOf(treeItems[0]);
}
Object data;
TreeItem[] items = parentItem.getItems();
for (int i = index; i < parentItem.getItemCount(); i++) {
data = items[i].getData();
if (data != null) {
displayPartName = data.toString();
break;
}
}
}
return displayPartName;
}
private Menu createPopupMenu_Brick(Decorations parent, final String filename) {
Menu menu = new Menu(parent, SWT.POP_UP);
final MenuItem modifyConnectivityItem = new MenuItem(menu, SWT.PUSH);
modifyConnectivityItem.setText("Modify Connectivity");
modifyConnectivityItem.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
ConnectivityEditorUI.getInstance(filename);
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
return menu;
}
private Menu createPopupMenu_Category(Decorations parent,
final String categoryName) {
Menu menu = new Menu(parent, SWT.POP_UP);
MenuItem modifyConnectivityItem = new MenuItem(menu, SWT.PUSH);
modifyConnectivityItem.setText("New Category");
modifyConnectivityItem.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
// open new dialog for input new category name
CategoryNameInputDialog dlg = new CategoryNameInputDialog(
shell, SWT.DIALOG_TRIM);
String categoryName = (String) dlg.open();
if (categoryName != null) {
cache.getCategories().add(categoryName);
cache.writeCategoryToFile();
cache.reload();
updatePartList();
updateListView();
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
modifyConnectivityItem = new MenuItem(menu, SWT.PUSH);
modifyConnectivityItem.setText("Delete");
modifyConnectivityItem.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
if (categoryName != null) {
cache.getCategories().remove(categoryName);
cache.writeCategoryToFile();
updateListView();
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
if (categoryName == null)
modifyConnectivityItem.setEnabled(false);
return menu;
}
private void generatePreview(Composite parent) {
parent.setLayout(new GridLayout(1, false));
parent.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true,
true));
previewGroup = new Composite(parent, SWT.NONE);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
previewGroup.setLayoutData(data);
previewGroup.setLayout(new GridLayout(10, true));
DragSource source = new DragSource(previewGroup, DND.DROP_COPY
| DND.DROP_MOVE);
source.setTransfer(new Transfer[] { TextTransfer.getInstance() });
source.addDragListener(this);
label_Preview = new Label(previewGroup, SWT.NULL);
data = new GridData(SWT.LEFT, SWT.TOP, false, false);
data.horizontalSpan = 8;
data.horizontalAlignment = GridData.FILL;
label_Preview.setLayoutData(data);
label_Preview.setText("");
colorButton = new Button(previewGroup, SWT.NONE);
data = new GridData(SWT.CENTER, SWT.CENTER, true, false);
data.horizontalSpan = 2;
colorButton.setLayoutData(data);
final ColorPicker colorPicker = new ColorPicker(colorButton,
"Set Preview Color");
colorPicker.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent event) {
Button selectedButton = (Button) event.widget;
LDrawColorT colorT = (LDrawColorT) selectedButton.getData();
colorPicker.setColor(colorT);
tree.notifyListeners(SWT.Selection, null);
selectedButton.getShell().setVisible(false);
}
@Override
public void widgetDefaultSelected(SelectionEvent event) {
}
});
colorButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent event) {
colorPicker.showDialog();
}
@Override
public void widgetDefaultSelected(SelectionEvent event) {
}
});
preview = new BrickViewer(previewGroup, tooltip);
data = new GridData(GridData.FILL, GridData.FILL, true, true);
data.horizontalSpan = 10;
preview.setLayoutData(data);
}
private boolean search(TreeItem[] parent, String searchText,
PartDomainT searchDomain) {
if (!searchText.equals(lastSearch) || searchDomain != lastSearchDomain) {
long nano = System.nanoTime();
updatePartList(searchText, searchDomain);
updateListView();
lastSearch = searchText;
lastSearchDomain = searchDomain;
// tree.setRedraw(true);
System.out.println("Search: " + searchText + "in "
+ (System.nanoTime() - nano) + " nano seconds");
updatePreview(getFirstPartName());
}
return true;
}
public boolean contains(String key) {
return cache.contains(key);
}
private void updateListView() {
tree.setVisible(false);
tree.setRedraw(false);
tree.removeAll();
ArrayList<String> categories = cache.getCategories();
TreeItem items[] = tree.getItems();
ArrayList<String> list;
items = new TreeItem[categories.size() + 1];
for (int i = 0; i < categories.size(); i++) {
list = partListMap.get(categories.get(i));
if (list == null)
continue;
if (lastSearch != null && lastSearch.equals("") == false
&& list.size() == 0)
continue;
items[i] = new TreeItem(tree, SWT.NONE);
items[i].setText(categories.get(i));
items[i].setImage(folderImage);
updateTreeItem(items[i], list);
}
tree.setRedraw(true);
tree.setVisible(true);
}
private void updateTreeItem(TreeItem parent, ArrayList<String> lists) {
final HashMap<String, ArrayList<String>> subCategoryMap = new HashMap<String, ArrayList<String>>();
final HashMap<String, Boolean> basisCheckMap = new HashMap<String, Boolean>();
String basisPartName;
for (String fileName : lists) {
basisPartName = LDrawUtilities.excludePattern(fileName);
if (fileName.equals(basisPartName)) {
subCategoryMap.put(basisPartName, new ArrayList<String>());
basisCheckMap.put(basisPartName, true);
} else if (subCategoryMap.containsKey(basisPartName) == false) {
subCategoryMap.put(basisPartName, new ArrayList<String>());
}
subCategoryMap.get(basisPartName).add(fileName);
}
for (String basisString : subCategoryMap.keySet()) {
Collections.sort(subCategoryMap.get(basisString),
new Comparator<String>() {
@Override
public int compare(String arg0, String arg1) {
String partDesc0;
String partDesc1;
partDesc0 = cache.getPartName(arg0);
partDesc1 = cache.getPartName(arg1);
return partDesc0.compareTo(partDesc1);
}
});
}
String categoryName;
String partDescription;
TreeItem item;
parent.removeAll();
ArrayList<String> categoryList = new ArrayList<String>(
subCategoryMap.keySet());
Collections.sort(categoryList, new Comparator<String>() {
@Override
public int compare(String arg0, String arg1) {
String partDesc0;
String partDesc1;
if (basisCheckMap.containsKey(arg0) && basisCheckMap.get(arg0)) {
partDesc0 = cache.getPartName(arg0);
} else {
partDesc0 = cache.getPartName(subCategoryMap.get(arg0).get(
0));
}
if (basisCheckMap.containsKey(arg1) && basisCheckMap.get(arg1)) {
partDesc1 = cache.getPartName(arg1);
} else {
partDesc1 = cache.getPartName(subCategoryMap.get(arg1).get(
0));
}
return partDesc0.compareTo(partDesc1);
}
});
for (String basisString : categoryList) {
ArrayList<String> subList = subCategoryMap.get(basisString);
if (basisCheckMap.containsKey(basisString)
&& basisCheckMap.get(basisString)) {
basisPartName = basisString;
} else {
basisPartName = subList.get(0);
}
categoryName = cache.getPartName(basisPartName);
item = new TreeItem(parent, SWT.NONE);
item.setText(categoryName);
item.setData(basisPartName);
if (subList.size() > 1)
for (String partName : subList) {
partDescription = cache.getPartName(partName);
TreeItem item2 = new TreeItem(item, SWT.NONE);
item2.setText(partDescription);
item2.setData(partName);
}
}
}
private void addBrick(String partName) {
LDrawPart part = new LDrawPart();
part.initWithPartName(partName, new Vector3f(0, 0, 0));
part.resolvePart();
part.moveTo(
new Vector3f(0, -part.boundingBox3().getMax()
.sub(part.boundingBox3().getMin()).y, 0),
LDrawGridTypeT.Coarse);
MOCBuilder.getInstance().addDirectiveToWorkingFile(part);
DirectiveAction action = new DirectiveAction();
action.addDirective(part);
LDrawUndoRedoManager.getInstance().pushUndoAction(action);
GlobalFocusManager.getInstance().forceFocusToMainView();
}
private void updatePreview(final String partName) {
final LDrawColor color = ColorLibrary.sharedColorLibrary()
.colorForCode((LDrawColorT) colorButton.getData());
Cursor waitCursor = display.getSystemCursor(SWT.CURSOR_WAIT);
shell.setCursor(waitCursor);
Thread thread = new Thread() {
@Override
public void run() {
if (partName == null) {
preview.setDirectiveToWorkingFile(null);
} else {
preview.setDirectiveToWorkingFile(partName, color);
}
display.asyncExec(new Runnable() {
@Override
public void run() {
if (partName == null)
label_Preview.setText("");
else
label_Preview.setText(partName + ": "
+ cache.getPartName(partName));
// label_Preview.pack();
if (!shell.isDisposed()) {
shell.setCursor(null);
}
}
});
}
};
thread.start();
}
@Override
public void dragStart(DragSourceEvent event) {
LDrawColor color = ColorLibrary.sharedColorLibrary().colorForCode(
(LDrawColorT) colorButton.getData());
DNDTransfer.getInstance().setColor(color);
event.image = null;
Object object = null;
Control control = ((DragSource) event.getSource()).getControl();
if (control.equals(previewGroup)) {
return;
} else if (control.equals(tree)) {
object = tree.getSelection()[0].getData();
}
if (object == null) {
event.doit = true;
event.data = "Category";
DNDTransfer.getInstance().setData("Category");
} else {
DNDTransfer.getInstance().setData(object);
}
}
@Override
public void dragSetData(DragSourceEvent event) {
if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
event.data = DNDTransfer.getInstance().getData();
}
}
@Override
public void dragFinished(DragSourceEvent event) {
DNDTransfer.getInstance().end();
}
void setDragAndDrop() {
Transfer[] types = new Transfer[] { TextTransfer.getInstance() };
int operations = DND.DROP_MOVE;
DropTarget target = new DropTarget(tree, operations);
target.setTransfer(types);
target.addDropListener(new DropTargetListener() {
@Override
public void dropAccept(DropTargetEvent event) {
}
@Override
public void drop(DropTargetEvent event) {
if (event.data == null || tree.getSelectionCount() == 0) {
event.detail = DND.DROP_NONE;
return;
}
TreeItem[] selectedItems = tree.getSelection();
TreeItem targetItem = null;
if (event.item != null) {
if (event.item.equals(selectedItems[0])) {
event.detail = DND.DROP_NONE;
return;
}
if (event.item.getData() == null) {
targetItem = (TreeItem) event.item;
} else {
}
}
if (selectedItems[0].getData() == null) { // src is category
ArrayList<String> categoryList = cache.getCategories();
int targetIndex = -1;
String srcItem = null;
srcItem = categoryList.get(tree.indexOf(selectedItems[0]));
if (srcItem != null)
categoryList.remove(srcItem);
if (targetItem == null) {
targetIndex = categoryList.size() - 1;
} else
for (int i = 0; i < categoryList.size(); i++) {
if (categoryList.get(i)
.equals(targetItem.getText())) {
targetIndex = i;
break;
}
}
if (targetIndex != -1) {
categoryList.add(targetIndex + 1, srcItem);
}
updateListView();
cache.writeCategoryToFile();
}
}
@Override
public void dragOver(DropTargetEvent event) {
if (DNDTransfer.getInstance().getData().equals("Category")) {
event.feedback = DND.FEEDBACK_INSERT_AFTER;
} else {
event.feedback = DND.FEEDBACK_NONE;
}
}
@Override
public void dragOperationChanged(DropTargetEvent event) {
}
@Override
public void dragLeave(DropTargetEvent event) {
}
@Override
public void dragEnter(DropTargetEvent event) {
}
});
}
}